/*
	File: CTileManager
	Author: Nolan McGlynn( GearMonster1 )
	Class: SGP1 1303
	Purpose: To Manage all the tiles that the player and enmies 
			 will interact with on the screen and render them accordingly
*/
#include "TileManager.h"
#include "../../TinyXML/tinyxml.h"
#include "../../SGD_Wrappers/CSGD_TextureManager.h"
#include "../../SGD_Wrappers/CSGD_Direct3D.h"
#include "../../SGD_Wrappers/SGD_String.h"
#include "../Game.h"
#include "../GameObjects/Entity.h"
#include "Tile.h"
#include "FireTile.h"
#include "HealingTile.h"
#include "GlassTile.h"
#include "IceTile.h"
#include "TarTile.h"
#include "../../SGD_Wrappers/CSGD_EventSystem.h"


CTileManager* CTileManager::GetInstance( void )
{
	//Store an instance in global memory
	static CTileManager s_Instance;

	//Return the instance
	return &s_Instance;
}


CTileManager::CTileManager(void)
{
	// Set the manager's and wrapper's pointers for easy access
	m_pTM = CSGD_TextureManager::GetInstance();
	m_D3D = CSGD_Direct3D::GetInstance();
}


CTileManager::~CTileManager(void)
{
}


void CTileManager::Render( void )
{
	for( unsigned int i = 0; i < m_vectorTilesFirstLayer.size( ); i++ )
	{
		if( m_vectorTilesFirstLayer[ i ]->GetWorldPosX()	 < CGame::GetInstance( )->GetWidth( )  + m_vectorTilesFirstLayer[ i ]->GetTileWidth( )  &&
				m_vectorTilesFirstLayer[ i ]->GetWorldPosY() < CGame::GetInstance( )->GetHeight( ) + m_vectorTilesFirstLayer[ i ]->GetTileHeight( ) &&
				m_vectorTilesFirstLayer[ i ]->GetWorldPosX() > -m_vectorTilesFirstLayer[ i ]->GetTileWidth( )  && 
				m_vectorTilesFirstLayer[ i ]->GetWorldPosY() > -m_vectorTilesFirstLayer[ i ]->GetTileHeight( ) )
				m_vectorTilesFirstLayer[ i ]->Render( );
	}
	for( unsigned int i = 0; i < m_vectorTilesSecondLayer.size( ); i++ )
	{
		if( m_vectorTilesSecondLayer[ i ]->GetWorldPosX()	 < CGame::GetInstance( )->GetWidth( )  + m_vectorTilesSecondLayer[ i ]->GetTileWidth( )  &&
				m_vectorTilesSecondLayer[ i ]->GetWorldPosY() < CGame::GetInstance( )->GetHeight( ) + m_vectorTilesSecondLayer[ i ]->GetTileHeight( ) &&
				m_vectorTilesSecondLayer[ i ]->GetWorldPosX() > -m_vectorTilesSecondLayer[ i ]->GetTileWidth( )  && 
				m_vectorTilesSecondLayer[ i ]->GetWorldPosY() > -m_vectorTilesSecondLayer[ i ]->GetTileHeight( ) )
				m_vectorTilesSecondLayer[ i ]->Render( );
	}
	for( unsigned int i = 0; i < m_vectorTilesThirdLayer.size( ); i++ )
	{
		if( m_vectorTilesThirdLayer[ i ]->GetWorldPosX()	 < CGame::GetInstance( )->GetWidth( )  + m_vectorTilesThirdLayer[ i ]->GetTileWidth( )  &&
				m_vectorTilesThirdLayer[ i ]->GetWorldPosY() < CGame::GetInstance( )->GetHeight( ) + m_vectorTilesThirdLayer[ i ]->GetTileHeight( ) &&
				m_vectorTilesThirdLayer[ i ]->GetWorldPosX() > -m_vectorTilesThirdLayer[ i ]->GetTileWidth( )  && 
				m_vectorTilesThirdLayer[ i ]->GetWorldPosY() > -m_vectorTilesThirdLayer[ i ]->GetTileHeight( ) )
				m_vectorTilesThirdLayer[ i ]->Render( );
	}
}

void CTileManager::ReadLevel( std::string XMLFilePath )
{
	// Create  TinyXML doucment Object
	TiXmlDocument TileMapXML;
	
	// Tell it where to get the file to read it from
	TileMapXML.LoadFile( XMLFilePath.c_str() );

	// Set the first node to the beginning root file in the XML file
	TiXmlElement* pNode = TileMapXML.RootElement();
	std::string FileName = pNode->Attribute( "File" );

	// Navigate through the first child till we get to something that is relevent 
	TiXmlElement* pImage = pNode->FirstChildElement( "TilesImage" );
	// Set up the file path for the tile set
	std::string ImageFilePath = "TileSets/";
	// Add the image name read from the XML file
	ImageFilePath += pImage->Attribute( "ImageName" );
	wchar_t WS[ 64 ];
	swprintf( WS, 64, L"%hs", ImageFilePath.c_str() );
	// Create a temporary image id to set the set of tiles to reference
	int m_nTileImageID = m_pTM->LoadTexture( WS );

	// Get the size of the world to define how many times we will have to loop through
	// to retrieve all tiles and total number of tiles
	TiXmlElement* pLevelSize = pImage->FirstChildElement( "LevelSize" );
	pLevelSize->Attribute( "Width", &m_nMapWidth );
	pLevelSize->Attribute( "Height", &m_nMapHeight );

	// Get the sizes of the tiles by reading them in
	TiXmlElement* pTileSize = pLevelSize->FirstChildElement( "TileSize" );
	pTileSize->Attribute( "Width", &m_nTileWidth );
	pTileSize->Attribute( "Height", &m_nTileHeight );

	// Navigate to the next child element
	TiXmlElement* pGroundLayer = pTileSize->FirstChildElement( "GroundLayer" );

	// Navigate from the ground layer to the tiles within this element
	TiXmlElement* pTile = pGroundLayer->FirstChildElement( "Tile" );

	// Create a set of temporary variables to catch the values read in from the xml
	int ImagePosX, ImagePosY;
	int WorldPosX, WorldPosY;
	int HasItem;
	CTile* someTile;

	// All tiles within this loop are ground base tiles i.e. largest loop no collision checks necessary
	while( pTile != nullptr )
	{

		std::string TileType = pTile->Attribute( "Name" );
		// set and assign the tiles and their types
		if( TileType == "Ground" )
		{		
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_GROUND );
		}
		else if( TileType == "Fire" )
		{
			someTile = new CFireTile;
			someTile->SetTileType( CTile::TILE_FIRE );
		}
		else if( TileType == "Ice" )
		{
			someTile = new CIceTile; 
			someTile->SetTileType( CTile::TILE_ICE );
		}
		else if( TileType == "Tar" )
		{
			someTile = new CTarTile;
			someTile->SetTileType( CTile::TILE_TAR );
		}
		else if( TileType == "Heal" )
		{
			someTile = new CHealingTile;
			someTile->SetTileType( CTile::TILE_HEAL );
		}
		else if( TileType == "Glass" )
		{
			someTile = new CHealingTile;
			someTile->SetTileType( CTile::TILE_GLASS );
		}
		else if( TileType == "Bottomless" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_BOTTOMLESS );
		}
		else if( TileType == "Wall" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_WALL );
		}
		else if( TileType == "Door" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_DOOR );
		}

		// Get the position of the tile in the image
		pTile->Attribute( "ImagePosX", &ImagePosX );
		pTile->Attribute( "ImagePosY", &ImagePosY );
		someTile->SetImagePosX( (float)ImagePosX );
		someTile->SetImagePosY( (float)ImagePosY );


		//Get the position of the tile in the world to be displayed on the screen
		pTile->Attribute( "WorldX", &WorldPosX );
		pTile->Attribute( "WorldY", &WorldPosY );
		someTile->SetWorldPosX( (float)WorldPosX );
		someTile->SetWorldPosY( (float)WorldPosY );

		// Set the widths and Heights of the tiles
		someTile->SetTileWidth( (float)m_nTileWidth );
		someTile->SetTileHeight( (float)m_nTileHeight );

		// Set the Image ID the tile comes from
		someTile->SetImageID( m_nTileImageID );


		m_vectorTilesFirstLayer.push_back( someTile );

		pTile = pTile->NextSiblingElement("Tile");
	}

	// Navigate to the next child element
	TiXmlElement* pEdgingLayer = pGroundLayer->NextSiblingElement("EdgingLayer");

	// Set pTile to the child elements of this layer
	pTile = pEdgingLayer->FirstChildElement("Tile");

	// All tiles in this loop are for tiles that have some form of edging
	while( pTile != nullptr )
	{

		std::string TileType = pTile->Attribute( "Name" );
		// set and assign the tiles and their types
		if( TileType == "Ground" )
		{		
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_GROUND );
		}
		else if( TileType == "Fire" )
		{
			someTile = new CFireTile;
			someTile->SetTileType( CTile::TILE_FIRE );
		}
		else if( TileType == "Ice" )
		{
			someTile = new CIceTile; 
			someTile->SetTileType( CTile::TILE_ICE );
		}
		else if( TileType == "Tar" )
		{
			someTile = new CTarTile;
			someTile->SetTileType( CTile::TILE_TAR );
		}
		else if( TileType == "Heal" )
		{
			someTile = new CHealingTile;
			someTile->SetTileType( CTile::TILE_HEAL );
		}
		else if( TileType == "Glass" )
		{
			someTile = new CHealingTile;
			someTile->SetTileType( CTile::TILE_GLASS );
		}
		else if( TileType == "Bottomless" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_BOTTOMLESS );
		}
		else if( TileType == "Wall" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_WALL );
		}
		else if( TileType == "Door" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_DOOR );
		}

		// Get the position of the tile in the image
		pTile->Attribute( "ImagePosX", &ImagePosX );
		pTile->Attribute( "ImagePosY", &ImagePosY );
		someTile->SetImagePosX( (float)ImagePosX );
		someTile->SetImagePosY( (float)ImagePosY );


		//Get the position of the tile in the world to be displayed on the screen
		pTile->Attribute( "WorldX", &WorldPosX );
		pTile->Attribute( "WorldY", &WorldPosY );
		someTile->SetWorldPosX( (float)WorldPosX );
		someTile->SetWorldPosY( (float)WorldPosY );

		// Set the widths and Heights of the tiles
		someTile->SetTileWidth( (float)m_nTileWidth );
		someTile->SetTileHeight( (float)m_nTileHeight );

		// Set the Image ID the tile comes from
		someTile->SetImageID( m_nTileImageID );


		m_vectorTilesSecondLayer.push_back( someTile );

		pTile = pTile->NextSiblingElement("Tile");
	}

	// Navigate to the next child element
	TiXmlElement* pEventLayer = pEdgingLayer->NextSiblingElement("EventLayer");

	pTile = pEventLayer->FirstChildElement("Tile");

	// All tiles in this loop are waypoints
	while( pTile != nullptr )
	{

		std::string TileType = pTile->Attribute( "Name" );
		// set and assign the tiles and their types
		if( TileType == "Ground" )
		{		
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_GROUND );
		}
		else if( TileType == "Fire" )
		{
			someTile = new CFireTile;
			someTile->SetTileType( CTile::TILE_FIRE );
		}
		else if( TileType == "Ice" )
		{
			someTile = new CIceTile; 
			someTile->SetTileType( CTile::TILE_ICE );
		}
		else if( TileType == "Tar" )
		{
			someTile = new CTarTile;
			someTile->SetTileType( CTile::TILE_TAR );
		}
		else if( TileType == "Heal" )
		{
			someTile = new CHealingTile;
			someTile->SetTileType( CTile::TILE_HEAL );
		}
		else if( TileType == "Glass" )
		{
			someTile = new CHealingTile;
			someTile->SetTileType( CTile::TILE_GLASS );
		}
		else if( TileType == "Bottomless" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_BOTTOMLESS );
		}
		else if( TileType == "Wall" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_WALL );
		}
		else if( TileType == "Door" )
		{
			someTile = new CTile;
			someTile->SetTileType( CTile::TILE_DOOR );
		}

		// Get the position of the tile in the image
		pTile->Attribute( "ImagePosX", &ImagePosX );
		pTile->Attribute( "ImagePosY", &ImagePosY );
		someTile->SetImagePosX( (float)ImagePosX );
		someTile->SetImagePosY( (float)ImagePosY );


		//Get the position of the tile in the world to be displayed on the screen
		pTile->Attribute( "WorldX", &WorldPosX );
		pTile->Attribute( "WorldY", &WorldPosY );
		someTile->SetWorldPosX( (float)WorldPosX );
		someTile->SetWorldPosY( (float)WorldPosY );

		// Set the widths and Heights of the tiles
		someTile->SetTileWidth( (float)m_nTileWidth );
		someTile->SetTileHeight( (float)m_nTileHeight );

		// Set the Image ID the tile comes from
		someTile->SetImageID( m_nTileImageID );

		// define what kind of item the tiles holds if any
		pTile->Attribute( "HasItem", &HasItem );
		someTile->SetItemType( HasItem );

		m_vectorTilesThirdLayer.push_back( someTile );	

		pTile = pTile->NextSiblingElement("Tile");
	}
}

void CTileManager::Shutdown( void )
{
	// Clear out each array of tiles by deleting the allocated memory
	for( unsigned int i = 0; i < m_vectorTilesFirstLayer.size( ); i++ )
	{
		delete m_vectorTilesFirstLayer[ i ];
	}

	for( unsigned int i = 0; i < m_vectorTilesSecondLayer.size( ); i++ )
	{
		delete m_vectorTilesSecondLayer[ i ];
	}

	for( unsigned int i = 0; i < m_vectorTilesThirdLayer.size( ); i++ )
	{
		delete m_vectorTilesThirdLayer[ i ];
	}
	// Clear the arrays so there is no longer garbage values or addresses in the arrays
	m_vectorTilesFirstLayer.clear();
	m_vectorTilesSecondLayer.clear();
	m_vectorTilesThirdLayer.clear();

	m_pTM->UnloadTexture( m_nTileImageID );
}

std::vector<CTile*> CTileManager::RecieveEventLayer( void )
{
	return m_vectorTilesThirdLayer;
}

void CTileManager::CheckCollsion( CEntity* pOther )
{
	RECT pOtherRect = pOther->GetRect();
	RECT overlap;
	for( unsigned int i =0; i< m_vectorTilesSecondLayer.size(); i++ )
	{
		if( (IntersectRect( &overlap, &m_vectorTilesSecondLayer[ i ]->GetRect(), &pOtherRect ) == TRUE))
		{
			switch( m_vectorTilesSecondLayer[ i ]->GetTileType() )
			{
			case CTile::TILE_BOTTOMLESS:
				{
					/// It's broken till a merge is complete but this is how the function would be called for
					/// the sent entity
					CSGD_EventSystem::GetInstance()->SendEvent("BOTTOMLESS",(void*)
						nullptr,
						(void*)pOther, (void*)m_vectorTilesSecondLayer[ i ] );
				}break;
			case CTile::TILE_FIRE:
				{
					/// It's broken till a merge is complete but this is how the function would be called for
					/// the sent entity
					CSGD_EventSystem::GetInstance()->SendEvent("FIRE",(void*)
						((int)(dynamic_cast<CFireTile*>(m_vectorTilesSecondLayer[ i ])->GetFireDamage())),
						(void*)pOther, (void*)m_vectorTilesSecondLayer[ i ] );
				}break;
			case CTile::TILE_HEAL:
				{
					/// It's broken till a merge is complete but this is how the function would be called for
					/// the sent entity
					CSGD_EventSystem::GetInstance()->SendEvent("HEAL",(void*)
						((int)(dynamic_cast<CHealingTile*>(m_vectorTilesSecondLayer[ i ])->GetHealAmount())),
						(void*)pOther, (void*)m_vectorTilesSecondLayer[ i ] );
				}break;
			case CTile::TILE_ICE:
				{
					/// It's broken till a merge is complete but this is how the function would be called for
					/// the sent entity
					CSGD_EventSystem::GetInstance()->SendEvent("ICE",nullptr, (void*)pOther, (void*)m_vectorTilesSecondLayer[ i ] );
				}break;
			case CTile::TILE_TAR:
				{
					/// It's broken till a merge is complete but this is how the function would be called for
					/// the sent entity
					CSGD_EventSystem::GetInstance()->SendEvent("TAR",nullptr, (void*)pOther, (void*)m_vectorTilesSecondLayer[ i ] );
				}break;
			case CTile::TILE_WALL:
				{
					CSGD_EventSystem::GetInstance()->SendEvent("WALL",nullptr, (void*)pOther, (void*)m_vectorTilesSecondLayer[ i ] );
				}break;
			default:
				{
					// SHOULD NEVER MAKE IT HERE, IF IT DOES YOU BROKE IT
				}break;
			}
		}
	}
}